package com.cskaoyan.javase.oop2._2polymorphic._2advantage;

/**
 * 多态的优缺点
 * 多态是面向对象三大特征之一，它基于继承而实现，是面向对象思想的集大成者。接下来我们研究一下，它的优缺点。我们通过一个案例：
 *
 * 科学家要收集动物的叫声去研究，先要收集猫，狗两种动物的叫声研究。随后又扩展到其它动物，最后又需要研究人类。
 *
 * 利用多态,给方法一个父类类型的形参,允许传入不同的子类对象
 * 这样做,就是利用多态的优点:
 * 从这个案例，我们其实就可以大致总结多态的优点了：
 *      1.要实现多态必须要继承，而继承提高了代码复用率，提升了程序的可维护性。（继承保证）
 *      2.有了多态后，用父类引用指向不同子类对象，只需要调用同名方法，就能自动根据不同子类对象，得出不同的行为。
 *          这大幅度简化了代码，提升了程序的扩展性。（多态保证）
 *
 * 新的需求:
 *      最后需要研究人类的叫声,人严格来说也是动物,它也可以继承Animal
 *      人类也有自己的叫声
 *      但是人比较特殊,人需要花钱请过来,让他协助研究
 *      说白了,人有自己独特的行为,叫"加钱"
 *
 * 多态的缺点:
 *      由于多态中，使用父类引用指向子类对象，父类引用限制了子类对象的功能，这意味着：
 *      子类独有的成员是无法使用父类引用访问的。
 *
 * 但是多态的缺点也是可以弥补的,怎么办呢?
 * 父类引用限制了子类对象的功能,于是干脆把父类引用转换成子类引用,这样做了一次强转,子类对象就能够恢复它的功能.
 *
 * 引用数据类型的强制类型转换(向下转型)
 * 语法:
 *      要强转成的类型类名 对象名 = (要强转成的类型类名) 引用名;
 *
 * 1.向下转型能够通过编译的条件是什么?
 *      只要是将这个父类引用
 *      强转成它的子类类型的引用
 *      就都能通过编译
 * 2.向下转型能够成功运行的条件是什么?
 *      我们发现任意做强转,会导致ClassCastException
 *      类型转换异常
 *      那么什么样的向下转型是可以成功的呢?
 *      成功的条件是:
 *      这个父类引用指向的对象
 *      必须是强转后的引用能够指向的对象
 *      或者换句话说
 *      这个父类引用指向的对象
 *      确实能够被强转后引用指向它
 *
 * 引用数据类型的强制类型转换(向下转型)是很危险的,成功的条件是很苛刻的,稍有不慎,就会导致ClassCastException
 *      程序抛出异常,导致程序终止执行
 *
 * 向下转型,如此危险,Java中提供了相应的语法来判断能否做强转,使用关键字instanceof
 * instanceof关键字的语法:
 *      引用名 instanceof 类名
 * 用于判断引用所指向的对象,是否是后面类名的对象或者子类对象
 * 如果是,该结构会返回true,表示这个引用可以强转成后面类名的引用
 * 如果不是,该结构会返回false,表示这个引用不可以强转成后面类名的引用
 * 这个结构常常配合if去使用
 *
 * @since 10:48
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Demo {
    public static void main(String[] args) {
        collectAnimalVoice(new Cat());
        collectAnimalVoice(new Dog());
        collectAnimalVoice(new Pig());
        collectAnimalVoice(new Person());
    }

    /*// 写一个方法收集研究猫的叫声
    public static void collectCatVoice(Cat c){
        // 抓一只猫过来,然后让它叫
        c.shout();
        // 收集研究叫声
    }*/

    // 写一个方法,用来收集研究所有动物的叫声
    public static void collectAnimalVoice(Animal a) {
        /*
            抓一只动物过来,让它叫
            但如果是人类需要先加钱,再给叫!
          */
        if (a instanceof Person){
            // 表示a指向的对象是Person的对象或者子类对象,那么a可以强转成Person引用
            Person p = (Person) a;
            p.addMoney();
        }
        a.shout();
        // 收集研究叫声
    }

}

class Animal {
    public void shout() {
        System.out.println("动物叫!");
    }
}

class Cat extends Animal {
    @Override
    public void shout() {
        System.out.println("喵喵喵~");
    }
}

class Dog extends Animal {
    @Override
    public void shout() {
        System.out.println("汪汪汪~");
    }
}

class Pig extends Animal {
    @Override
    public void shout() {
        System.out.println("吼吼吼~");
    }
}

class Person extends Animal {
    @Override
    public void shout() {
        System.out.println("嘤嘤嘤~");
    }

    // 加钱,人类独有的方法,是子类独有的方法
    public void addMoney(){
        System.out.println("这种事情,人家很没面子,让我叫,得加钱!");
    }
}